
#include "Gzip.h"
#include "zlib/zlib.h"


bool GZip::compress(const char *src, size_t length, const OUTER& outbuffer)
{
    z_stream stream;
    stream.zalloc = Z_NULL;
    stream.zfree = Z_NULL;
    stream.opaque = Z_NULL;

    if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
        MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
    {
        return false;
    }

    static char gz_simple_header[] = { '\037', '\213', '\010', '\000', '\000', '\000', '\000', '\000', '\002', '\377' };

    uint8_t out[4096] = { 0 };
    outbuffer(gz_simple_header, sizeof(gz_simple_header));

    stream.next_in = (Bytef *)src;
    stream.avail_in = uInt(length);

    do 
    {
        stream.next_out = (Bytef *)out;
        stream.avail_out = 4096;
        int r = deflate(&stream, Z_FINISH);
        switch (r)
        {
        case Z_NEED_DICT:    /* and fall through */
        case Z_DATA_ERROR:
        case Z_MEM_ERROR:
            deflateEnd(&stream);
            return false;
        }
        outbuffer((char*)out, 4096 - stream.avail_out);
    } while (stream.avail_out == 0);

    uLong  crc = crc32(0, Z_NULL, 0);
    crc = crc32(crc, (const Bytef *)src, uInt(length));
    outbuffer((char*)&crc, 4);
    outbuffer((char*)&length, 4);

    deflateEnd(&stream);
    return true;
}

bool GZip::uncompress(const char *src, size_t length, const OUTER& outbuffer)
{
    z_stream strm;

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;

    int ret = inflateInit2(&strm, 47);

    if (ret != Z_OK) {
        return false;
    }

    strm.avail_in = uInt(length);
    strm.next_in = (unsigned char *)src;

    uint8_t out[4096] = { 0 };  // 4K
   
    /* run inflate() on input until output buffer not full */
    do
    {
        strm.avail_out = 4096;
        strm.next_out = out;

        ret = inflate(&strm, Z_NO_FLUSH);
		if (ret == Z_STREAM_ERROR)
		{
			inflateEnd(&strm);
			return false;
		}
        //assert(ret != Z_STREAM_ERROR); /* state not clobbered */
        switch (ret)
        {
        case Z_NEED_DICT:    /* and fall through */
        case Z_DATA_ERROR:
        case Z_MEM_ERROR:
            inflateEnd(&strm);
            return false;
        }
        outbuffer((char *)out, 4096 - strm.avail_out);
        //           buffer.insert(buffer.end(), (char *)out, (char *)out + CHUNK - strm.avail_out);
    } while (strm.avail_out == 0);

    /* clean up and return */
    inflateEnd(&strm);

    return (ret == Z_STREAM_END);
}

bool Flate::uncompress(const char *zdata, size_t nzdata, std::string& outbuffer)
{
	int err = 0;
	z_stream d_stream = { 0 }; /* decompression stream */

	static char dummy_head[2] = {
		0x8 + 0x7 * 0x10,
		(((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
	};

	d_stream.zalloc = NULL;
	d_stream.zfree = NULL;
	d_stream.opaque = NULL;
	d_stream.next_in = (Bytef*)zdata;
	d_stream.avail_in = uInt(nzdata);
	//d_stream.next_out = data;


	if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK) {
		return false;
	}

	// if(inflateInit2(&d_stream, 47) != Z_OK) return -1;

	uint8_t out[4096] = { 0 };  // 4K
	int ret = 0;
	do
	{
		d_stream.avail_out = 4096;
		d_stream.next_out = out;

		ret = inflate(&d_stream, Z_NO_FLUSH);

		//assert(ret != Z_STREAM_ERROR); /* state not clobbered */
		if (ret == Z_STREAM_ERROR) {
			inflateEnd(&d_stream);
			return false;
		}
		switch (ret)
		{
		case Z_NEED_DICT:    /* and fall through */
		case Z_DATA_ERROR:
		case Z_MEM_ERROR:
			inflateEnd(&d_stream);
			return false;
		}
		outbuffer.append((char *)out, 4096 - d_stream.avail_out);
	} while (d_stream.avail_out == 0);


	/* clean up and return */
	inflateEnd(&d_stream);

	return (ret == Z_STREAM_END);
}
